package com.ihr360.payroll.service.salarytasklet.step;

import com.ihr360.alps.api.ServiceProvider;
import com.ihr360.commons.exception.Ihr360Exception;
import com.ihr360.commons.lang.CollectionUtils;
import com.ihr360.commons.lang.StringUtils;
import com.ihr360.commons.model.type.FieldSource;
import com.ihr360.commons.model.type.FormulaFrom;
import com.ihr360.expr.ExprException;
import com.ihr360.expr.engine.GraphTraversalListener;
import com.ihr360.expr.engine.VariableID;
import com.ihr360.payroll.model.dto.salary.basecode.SalaryFieldType;
import com.ihr360.payroll.model.dto.salary.basecode.SalaryTaxType;
import com.ihr360.payroll.model.dto.salary.embeddable.SalaryFieldValue;
import com.ihr360.payroll.model.dto.salary.embeddable.SystemSalaryFieldValue;
import com.ihr360.payroll.model.dto.salary.meta.SalaryField;
import com.ihr360.payroll.model.dto.salary.meta.system.SystemSalaryField;
import com.ihr360.payroll.model.type.SalaryPhase;
import com.ihr360.payroll.service.salarytasklet.expr.engine.SalaryDependencyEngine;
import com.ihr360.payroll.service.salarytasklet.listener.ILoggerCallback;
import com.ihr360.payroll.service.salarytasklet.step.utils.SalaryCodeUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

public class SalaryFieldDependencyChecker {
    final static Logger logger = LoggerFactory.getLogger(SalaryFieldDependencyChecker.class.getName());

    /**
     * 检查循环依赖
     *
     * @param engine
     * @param salaryFields
     * @param isMergeTaxTerm
     * @param loggerCallback
     * @param calcContext
     * @param planName
     * @throws ExprException
     */
    public static void check(SalaryDependencyEngine engine, Map<String, SalaryField> salaryFields, boolean isMergeTaxTerm, ILoggerCallback loggerCallback, SalaryCalcContext calcContext, String planName) throws ExprException {
        //设置工资表中的薪资字段进入表达式引擎(前提条件:薪资表中的所有项目的依赖薪资项目(不包含引用其他项目)必须在薪资表中存在)
        Map<String, SalaryField> noneFormulaSalaryFields = new HashMap<>(50);
        Set<String> taxFieldCodes = new HashSet<>(5);
        if (CollectionUtils.isNotEmpty(salaryFields)) {
            Optional<SalaryField> sihfOptional = salaryFields.values().stream().filter(s -> s.getType() != null && (SalaryFieldType.Code.personsihf.equals(s.getType().getCode()) || SalaryFieldType.Code.companysihf.equals(s.getType().getCode()))).findFirst();
            if (sihfOptional.isPresent()) {
                if (calcContext != null) {
                    calcContext.hasSIHFFields = true;
                }
            }
            Optional<SalaryField> attendanceOptional = salaryFields.values().stream().filter(s -> s.getType() != null && (SalaryFieldType.Code.attendance.equals(s.getType().getCode()))).findFirst();
            if (attendanceOptional.isPresent()) {
                if (calcContext != null) {
                    calcContext.hasAttendanceFields = true;
                }
            }
            for (SalaryField field : salaryFields.values()) {
                SalaryTaxType taxType = field.getTaxType();
                if (taxType != null) {
                    String taxTypeCode = taxType.getCode();
                    String fieldCode = SalaryCodeUtils.salaryTaxTypeCodeToSalaryFieldCode(taxTypeCode);
                    if (!taxFieldCodes.contains(fieldCode)) {
                        taxFieldCodes.add(fieldCode);
                    }
                }

                SalaryFieldValue value = field.getValueMeta();
                String formula = value.getFormula();
                FieldSource source = field.getSource();
                if (StringUtils.isNotEmpty(formula) && FieldSource.FUNCTION.equals(source)) {
                    logger.debug("{} formula {}", field.getCode(), formula);
                    try {
                        if (!isMergeTaxTerm && field.isTaxMergeField()) {
                            if (FormulaFrom.USR.equals(value.getFormulaFrom())) {
                                engine.set(field.getCode(), formula);
                            } else {
                                // engine.set(field.getCode(), "0.0");
                            }
                        } else {
//                            if (mergeTaxTerm) {
//                                formula.replaceAll(SalaryCalcJobConfig.BASED_ON_PLAN_PLACEHOLDER, planName);
//                            }
                            engine.set(field.getCode(), formula);
                        }
                    } catch (ExprException e) {
                        throw new Ihr360Exception(null, e.getMessage(), e);
                    }

                } else {
                    noneFormulaSalaryFields.put(field.getCode(), field);
                }
                String mergeTaxFormula = value.getMergeTaxFormula();
                //合并计税公式和非合并计税公式的分别设置
                if (isMergeTaxTerm && StringUtils.isNotBlank(mergeTaxFormula)) {
//                    if (mergeTaxFormula.contains(SalaryCalcJobConfig.BASED_ON_PLAN_PLACEHOLDER)) {
//                        mergeTaxFormula = mergeTaxFormula.replaceAll(SalaryCalcJobConfig.BASED_ON_PLAN_PLACEHOLDER, planName);
//                    }
                    logger.debug("{} MergeTaxFormula {}", field.getCode(), mergeTaxFormula);

                    try {
                        engine.set(field.getCode(), mergeTaxFormula);
                    } catch (ExprException e) {
                        throw new Ihr360Exception(null, e.getMessage(), e);
                    }
                }
            }
        }

        //添加系统过渡变量到表达式引擎
        List<SystemSalaryField> transientSalaryFields = calcContext.getSalaryFieldsOfType(SalaryFieldType.Code.Transient);
        addSystemFieldsToDependencyToEngine(engine, salaryFields, transientSalaryFields, isMergeTaxTerm, loggerCallback, planName);
        //增加统计项目变量到表达式引擎
        List<SystemSalaryField> statisticsSalaryFields = calcContext.getSalaryFieldsOfType(SalaryFieldType.Code.statistics);
        addSystemFieldsToDependencyToEngine(engine, salaryFields, statisticsSalaryFields, isMergeTaxTerm, loggerCallback, planName);
        //添加考勤项目进入
        List<SystemSalaryField> attendanceSalaryFields = calcContext.getSalaryFieldsOfType(SalaryFieldType.Code.attendance);
        addSystemFieldsToDependencyToEngine(engine, salaryFields, attendanceSalaryFields, isMergeTaxTerm, loggerCallback, planName);
        //添加税费项目
        List<SystemSalaryField> taxFields = calcContext.getSalaryFieldsOfType(SalaryFieldType.Code.tax);
        List<SystemSalaryField> usedSystemSalaryFields = taxFields.stream().filter(s -> taxFieldCodes.contains(s.getCode())).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(usedSystemSalaryFields)) {
            addSystemFieldsToDependencyToEngine(engine, salaryFields, usedSystemSalaryFields, isMergeTaxTerm, loggerCallback, planName);
        }


        engine.traverseGraph(new GraphTraversalListener() {
            @Override
            public void traverse(VariableID node) {
                //查找包含的变量
                if (engine.containsVariable(node)) {
                    return;
                }
                String variableId = node.getVariableId();
                if (noneFormulaSalaryFields.containsKey(variableId)) {
                    return;
                }

                if (node.hasSheet() && isNotPhase(node.getSheet())) {
                    //依赖于其他批次的项目
                    engine.addTermReference(node.getField());
                    return;
                }

                if (node.hasNamespece()) {
                    //依赖与其他服务的项目
                    if (!calcContext.isReferenceBenefitData && node.getNamespace().equals(ServiceProvider.SERVICE_ID.SIHF)) {
                        return;
                    }
                    engine.addSalaryReference(node.getNamespace(), node.getField());
                    return;
                }
                String variableName = calcContext.getSystemSalaryFieldName(variableId);
                String content = "依赖薪资项目[" + variableName + ":" + node.getVariableId() + "],在薪资表中不存在";
                if (loggerCallback != null) {
                    loggerCallback.trace("[公式初始化]", content);
                }
                logger.info(node.getVariableId());
            }
        });
    }

    private static boolean isNotPhase(String sheet) {
        return !sheet.equals(SalaryPhase.PHASE1.toString()) && !sheet.equals(SalaryPhase.PHASE2.toString());
    }


    private static void addSystemFieldsToDependencyToEngine(SalaryDependencyEngine engine, Map<String, SalaryField> salaryFields, List<SystemSalaryField> systemSalaryFields, boolean mergeTaxTerm, ILoggerCallback loggerCallback, String planName) throws ExprException {

        if (CollectionUtils.isNotEmpty(systemSalaryFields)) {
            for (SystemSalaryField field : systemSalaryFields) {
                String code = field.getCode();
                if (salaryFields.containsKey(code)) {
                    continue;
                }
                SystemSalaryFieldValue value = field.getValueMeta();
                String mergeTaxFormula = value.getMergeTaxFormula();
                //合并计税公式和非合并计税公式的分别设置
                if (mergeTaxTerm && StringUtils.isNotBlank(mergeTaxFormula)) {
//                    if (mergeTaxFormula.contains(SalaryCalcJobConfig.BASED_ON_PLAN_PLACEHOLDER)) {
//                        mergeTaxFormula = mergeTaxFormula.replaceAll(SalaryCalcJobConfig.BASED_ON_PLAN_PLACEHOLDER, planName);
//                    }
                    logger.debug("{} MergeTaxFormula {}", code, mergeTaxFormula);
                    engine.set(code, mergeTaxFormula);
                } else {
                    String formula = value.getFormula();
                    FieldSource source = value.getDefaultSource();
                    if (StringUtils.isNotEmpty(formula) && FieldSource.FUNCTION.equals(source)) {
//                        if (mergeTaxTerm) {
//                            formula = formula.replaceAll(SalaryCalcJobConfig.BASED_ON_PLAN_PLACEHOLDER, planName);
//                        }
                        if (loggerCallback != null) {
                            loggerCallback.trace("[公式初始化]", "临时增加系统项目 " + field.getName());
                        }
                        engine.set(code, formula);
                    }
                }
            }
        }
    }

}